﻿using System;
using System.DirectoryServices;

namespace Library
{
  #region DirectoryObjectクラス
  /// <summary>
  /// Directory オブジェクトを表す抽象基本クラスです。
  /// </summary>
  public abstract class DirectoryObject : IDirectory
  {
    #region プライベートフィールド
    private readonly DirectoryEntry _entry;
    private readonly CategoryType _category;
    private readonly string _name;
    private bool _disposed;
    #endregion

    #region プロテクトコンストラクター
    /// <summary>
    /// <see cref="DirectoryEntry"/> を指定して <see cref="DirectoryObject"/> クラスの新しいインスタンスを初期化します。
    /// </summary>
    /// <param name="entry"><see cref="DirectoryEntry"/>。</param>
    /// <exception cref="ArgumentNullException"><paramref name="entry"/> が null です。</exception>
    /// <exception cref="ArgumentException"><paramref name="entry"/> の種類が <see cref="CategoryType"/> に該当しません。</exception>
    protected DirectoryObject(DirectoryEntry entry)
    {
      if (entry == null)
      {
        throw new ArgumentNullException("entry", "entry が null です。");
      }
      if (Enum.TryParse<CategoryType>(entry.SchemaClassName, true, out _category) == false)
      {
        throw new ArgumentException("entry の種類が CategoryType に該当しません。", "entry");
      }

      _entry = entry;
      _name = _entry.Name;
    }
    #endregion

    #region パブリックプロパティ
    /// <summary>
    /// Directory オブジェクトの種類を取得します。
    /// </summary>
    public CategoryType Category
    {
      get
      {
        return _category;
      }
    }

    /// <summary>
    /// 説明を取得または設定します。
    /// </summary>
    public string Description
    {
      get
      {
        if (_disposed)
        {
          throw new ObjectDisposedException(this.GetType().Name);
        }
        return (string)_entry.Properties["description"].Value;
      }
      set
      {
        if (_disposed)
        {
          throw new ObjectDisposedException(this.GetType().Name);
        }
        _entry.Properties["description"].Value = value;
      }
    }

    /// <summary>
    /// 関連付けられた <see cref="DirectoryEntry"/> を取得します。
    /// </summary>
    public DirectoryEntry Entry
    {
      get
      {
        if (_disposed)
        {
          throw new ObjectDisposedException(this.GetType().Name);
        }
        return _entry;
      }
    }

    /// <summary>
    /// オブジェクトが破棄されているかどうかを取得します。
    /// </summary>
    public bool IsDisposed
    {
      get
      {
        return _disposed;
      }
    }

    /// <summary>
    /// 名前を取得します。
    /// </summary>
    public virtual string Name
    {
      get
      {
        return _name;
      }
    }
    #endregion

    #region パブリックメソッド
    /// <summary>
    /// 使用されているリソースを解放します。
    /// </summary>
    public void Dispose()
    {
      this.Dispose(true);
      GC.SuppressFinalize(this);
    }

    /// <summary>
    /// 名前を返します。
    /// </summary>
    /// <returns>名前。</returns>
    public override string ToString()
    {
      return this.Name;
    }
    #endregion

    #region プロテクトメソッド
    /// <summary>
    /// 使用されているリソースを解放し、オプションでマネージ リソースも解放します。
    /// </summary>
    /// <param name="disposing">マネージ リソースとアンマネージ リソースの両方を解放する場合は true。
    /// アンマネージ リソースだけを解放する場合は false。</param>
    protected virtual void Dispose(bool disposing)
    {
      if (_disposed == false)
      {
        if (disposing)
        {
          _entry.Close();
        }
        _disposed = true;
      }
    }
    #endregion
  }
  #endregion

  #region DomainObjectクラス
  /// <summary>
  /// ドメインの Directory オブジェクトを表す抽象基本クラスです。
  /// </summary>
  public abstract class DomainObject : DirectoryObject, IDomain
  {
    #region プライベートフィールド
    private readonly string _path, _name;
    #endregion

    #region プロテクトコンストラクター
    /// <summary>
    /// <see cref="DirectoryEntry"/> を指定して <see cref="DomainObject"/> クラスの新しいインスタンスを初期化します。
    /// </summary>
    /// <param name="entry"><see cref="DirectoryEntry"/>。</param>
    /// <exception cref="ArgumentNullException"><paramref name="entry"/> が null です。</exception>
    /// <exception cref="ArgumentException"><paramref name="entry"/> の種類が <see cref="CategoryType"/> に該当しません。</exception>
    protected DomainObject(DirectoryEntry entry)
      : base(entry)
    {
      _path = DirectoryAccess.GetBelongPath(entry);
      _name = entry.Properties["name"].Value.ToString();
    }
    #endregion

    #region パブリックプロパティ
    /// <summary>
    /// 表示用の所属パスを取得します。
    /// </summary>
    public string DisplayPath
    {
      get
      {
        return _path;
      }
    }

    /// <summary>
    /// 表示用の名前を取得します。
    /// </summary>
    public override string Name
    {
      get
      {
        return _name;
      }
    }
    #endregion
  }
  #endregion
}
